#include "archivoTerminos.h"

archivoTerminos::archivoTerminos(string ruta) : indice(ruta)
{
	arch.open(ruta.c_str(), fstream::in|fstream::out|fstream::binary);
	if(!arch.is_open())
	{
		arch.clear();
		arch.open(ruta.c_str(), fstream::out|fstream::binary);
		arch.seekp(0,fstream::beg);
		cantPalabras = 0;
		arch.write((char*) &cantPalabras, sizeof(cantPalabras));
		arch.close();
		arch.clear();
		arch.open(ruta.c_str(), fstream::in|fstream::out|fstream::binary);
	}
	arch.seekg(0,fstream::beg);
	arch.read((char*) &cantPalabras, sizeof(cantPalabras));
}

archivoTerminos::~archivoTerminos()
{
	arch.seekp(0,fstream::beg);
	arch.write((char*) &cantPalabras, sizeof(cantPalabras));
}


long archivoTerminos::agregarPalabra(string& palabra)
{
	ClaveStr clave;
	DatoLong dato;
	string subpalInicio, subpalFin;
	long offset, offsetSig, id=0;
	unsigned cantChars;

	if (palabra.length()>=TAM_MAX_PALABRA)
	{
		// cuando se excede la longitud maxima se separa la palabra
		subpalInicio = palabra.substr(0,TAM_MAX_PALABRA);
		subpalFin = palabra.substr(TAM_MAX_PALABRA, palabra.length()-TAM_MAX_PALABRA);
		clave = subpalInicio;
		
		// se consulta si ya esta en el indice la parte inicial de la palabra
		indice.readIgual(dato,clave);
		if(indice.eof())
		{
			// si todavia no se agrego ...
			id = ++cantPalabras;
			
			// se posiciona al final del stream y se obtiene esa posicion
			arch.seekp(0,fstream::end);
			offset = arch.tellg();
			
			// es la ultima del encodenamiento
			offsetSig = 0;
			
			// se escribe la longitud
			cantChars = subpalFin.length();
			arch.write((char*) &cantChars, sizeof(cantChars));
			
			// si la longitud es 0 no se escribe nada
			if(cantChars>0)
				arch.write((char*) subpalFin.c_str(), cantChars);
			
			// se escribe el id y el offset al siguiente
			arch.write((char*) &id, sizeof(long));
			arch.write((char*) &offsetSig, sizeof(long));
			dato = offset;
			
			// se inserta en el indice el offset en el stream como dato
			// y como clave la primera parte de la palabra
			indice.insert(clave, dato);
		}
		else
		{
			// si ya esta en el indice se posiciona en el stream segun
			// el offset que se obtiene del indice
			arch.seekg(dato.getDato());
			
			// se leen los datos
			arch.read((char*) &cantChars, sizeof(cantChars));
			char* cadena = new char[cantChars+1];
			string str;
			if(cantChars>0)
			//{
				arch.read((char*) cadena, cantChars);
				str = string(cadena,0,cantChars);
			//}
			//else
			//	str = "";
			arch.read((char*) &id, sizeof(long));
			long dirOffsetSig = arch.tellp();
			arch.read((char*) &offsetSig, sizeof(long));
			
			// encontrado indica cuando se localiza la segunda parte de la palabra
			bool encontrado = (str==subpalFin);
			
			// si no se encontro en la primera lectura se inicia un ciclo de busqueda
			// en el encadenamiento, que termina cuando se encuentra o cuando termina
			// el encadenamiento
			while(offsetSig!=0 && !encontrado)
			{
				arch.seekg(offsetSig);
				arch.read((char*) &cantChars, sizeof(cantChars));
				delete[] cadena;
				cadena = new char[cantChars+1];
				if(cantChars>0)
			//	{
					arch.read((char*) cadena, cantChars);
					str = string(cadena,0,cantChars);
			//	}
			//	else
			//		str = "";
				arch.read((char*) &id, sizeof(long));
				dirOffsetSig = arch.tellp();
				arch.read((char*) &offsetSig, sizeof(long));
				encontrado = (str==subpalFin);
			}
			
			// si no se encuentra ...
			if(!encontrado)
			{
				// se agrega manteniendo el encadenamiento
				id = ++cantPalabras;
				arch.seekp(0,fstream::end);
				offset = arch.tellg();
				offsetSig = 0;
				cantChars = subpalFin.length();
				arch.write((char*) &cantChars, sizeof(cantChars));
				if(cantChars>0)
					arch.write((char*) subpalFin.c_str(), cantChars);
				arch.write((char*) &id, sizeof(long));
				arch.write((char*) &offsetSig, sizeof(long));
				arch.seekp(dirOffsetSig);
				arch.write((char*) &offset, sizeof(long));
				dato = offset;
				indice.insert(clave, dato);
			}
			delete[] cadena;
		}
	}
	else
	{
		// si la palabra no excede TAM_MAX_PALABRA se inserta en indice 
		// normalmente con el dato conteniendo el id de la palabra
		clave=palabra;
		indice.readIgual(dato, clave);
		if (indice.eof())
		{
			id = ++cantPalabras;
			dato = id;
			indice.insert(clave, dato);
		}
		else
			id = dato.getDato();
	}
	return id;
}

long archivoTerminos::obtenerIdPalabra(string& palabra)
{
	ClaveStr clave;
	DatoLong dato;
	string subpalInicio, subpalFin;
	long offsetSig, id=0;
	unsigned cantChars;
	if (palabra.length()>=TAM_MAX_PALABRA)
	{
		subpalInicio = palabra.substr(0,TAM_MAX_PALABRA);
		subpalFin = palabra.substr(TAM_MAX_PALABRA, palabra.length()-TAM_MAX_PALABRA);
		clave = subpalInicio;
		indice.readIgual(dato,clave);
		if(!indice.eof())
		{
			arch.seekg(dato.getDato());
			arch.read((char*) &cantChars, sizeof(cantChars));
			char* cadena = new char[cantChars+1];
			string str;
			if(cantChars>0)
		//	{
				arch.read((char*) cadena, cantChars);
				str = string(cadena,0,cantChars);
		//	}
		//	else
		//		str = "";
			arch.read((char*) &id, sizeof(long));
			arch.read((char*) &offsetSig, sizeof(long));
			
			bool encontrado = (str==subpalFin);
			
			while(offsetSig!=0 && !encontrado)
			{
				arch.seekg(offsetSig);
				arch.read((char*) &cantChars, sizeof(cantChars));
				delete[] cadena;
				cadena = new char[cantChars+1];
				if(cantChars>0)
		//		{
					arch.read((char*) cadena, cantChars);
					str = string(cadena,0,cantChars);
		//		}
		//		else
		//			str = "";
				arch.read((char*) &id, sizeof(long));
				arch.read((char*) &offsetSig, sizeof(long));
			
				encontrado = (str==subpalFin);
			}
			
			if(!encontrado)
				id = 0;
			delete[] cadena;
		}
	}
	else
	{
		clave=palabra;
		indice.readIgual(dato, clave);
		if (!indice.eof())
			id = dato.getDato();
	}
	return id;
}